<script setup lang="ts">
import {defineProps, computed, toRefs, ref, defineEmits} from 'vue'
import {NUpload, UploadInst, UploadFileInfo, useMessage} from 'naive-ui'
import {Close} from '@vicons/carbon'
import {FileUploadRound, CheckCircleRound} from '@vicons/material'
import {DocumentTableCheckmark20Filled} from '@vicons/fluent'

import {ThemesColor} from '@/config';
import {uploadFileRequest, postRequest} from '@/api/data';
import {v4 as uuidv4} from 'uuid'
import {isString} from "@/util/is.ts";

const message = useMessage();


interface MergeChunks {
  original_name: string,
  file_name: string,
  totalChunks: number,
  group_id?: number,
  insert?: boolean,
}

const emits = defineEmits(['update:value', 'finished'])
const props = defineProps({
  ...NUpload.props,
  value: {
    type: [Array, String, null],
    default: () => null,
  },
  action: {
    type: String,
    default: 'sys/sub_up_file'
  },
  type: {
    type: String,
    default: undefined, //框(box)，按钮(button),文本(text),自定义内容(slot)
  },
  upFileType: {
    type: String,
    default: undefined, //所需上传的文件类型  如:zip,pdf等
  },
  uploadText: {
    type: String,
    default: '文件'
  },
  width: {
    type: Number,
    default: 70
  },
  height: {
    type: Number,
    default: 70
  },
  borderStyle: {
    type: String,
    default: "dashed"
  },
  fileUuid: {
    type: Boolean,
    default: true,//文件名是否改成uuid  true:是  false:否
  },
  upLocal: {
    type: Boolean,
    default: false,//是否上传至本地  true:是(强制)  false:跟随系统配置
  },
  uploadType: {
    type: String,
    default: "",//上传文件类型
  },
  groupId: {
    type: String,
    default: "-1",
  },
  insert: {
    type: Boolean,
    default: false,//是否写入数据表
  },
})
const {accept, upFileType, data, fileUuid, action, multiple, value} = toRefs(props)
const uploadRef = ref<UploadInst | null>(null)
//触发区域div
const triggerDiv = ref<HTMLElement | null>(null)
const chunkSize = 2 * 1024 * 1024;
//处理可上传文件类型
const acceptType = computed(() => {
  if (accept.value) {
    return accept.value
  } else if (accept.value == undefined && upFileType.value) {
    return processFileExtensions(upFileType.value)
  }
  return undefined
})
//预处理附加数据
const preprocessingData = computed(() => {
  return {...data.value, ...{uuid: fileUuid.value}};
})
const hasFile = computed(() => {
  if (!value.value) {
    return false;
  }
  if (Array.isArray(value.value)) {
    return value.value.length > 0;
  }
  return isString(value.value);
});
//处理可上传文件格式
const processFileExtensions = (input: string) => {
  return input.split(',').map(ext => {
    // 去除前后空格
    ext = ext.trim();
    // 检查是否以 '.' 开头，如果没有则添加
    if (!ext.startsWith('.')) {
      ext = '.' + ext;
    }
    // 返回原始和大写形式
    return `${ext},${ext.toUpperCase()}`;
  }).join(',');
}
//点击外按键触发上传
const click = () => {
  if (triggerDiv.value) {
    triggerDiv.value.click();
  }
}
//文件分段上传
const uploadFile = async (options: { file: UploadFileInfo, fileList: Array<UploadFileInfo>, event?: Event }) => {
  const file_info = options.file.file;

  if (!file_info) {
    return false;
  }
  const file_size = file_info.size
  //上传图片大小
  if (file_size > (1024 * 1024 * 2) && props.uploadType == "img") {
    message.error("上传的图片大小不得超过2MB")
    return false;
  }
  const fileUuid = uuidv4(); // 生成 UUID
  const totalChunks = Math.ceil(file_size / chunkSize);
  const uploadPromises: any[] = [];

  let uploadFiles: MergeChunks | any = {};

  //处理upLocal
  const up_local = props.upLocal
  for (let i = 0; i < totalChunks; i++) {
    const start = i * chunkSize;
    const end = Math.min(file_size, start + chunkSize);
    const chunk = file_info.slice(start, end);
    const formData = new FormData();
    formData.append('chunk', chunk);
    formData.append('chunkIndex', i.toString());
    formData.append('totalChunks', totalChunks.toString());
    formData.append('fileName', file_info.name);
    formData.append('fileUuid', fileUuid);
    formData.append('upLocal', up_local.toString());
    //遍历附带的参数
    for (const key in preprocessingData.value) {
      formData.append(`${key}`, `${preprocessingData.value[key]}`);
    }
    uploadPromises.push(uploadFileRequest(action.value, formData).then((res: ResponseResult) => {
      const {data} = res
      if (res.code == 200) {
        uploadFiles = {original_name: data.original_name, file_name: data.file_name, totalChunks: totalChunks}
      }
    }))
  }
  try {
    let finish = false;
    await Promise.all(uploadPromises);
    await postRequest('sys/merge_chunks', {
      original_name: uploadFiles.original_name,
      file_name: uploadFiles.file_name,
      totalChunks: uploadFiles.totalChunks,
      upLocal: props.upLocal,
      group_id: props.groupId,
      insert: props.insert,
    }).then((result: ResponseResult) => {
      if (result.code == 200) {
        if (multiple.value) {
          emits('update:value', [...value.value, uploadFiles.file_name]);
        } else {
          emits('update:value', uploadFiles.file_name);
        }
        finish = true;
      }
    })
    if (finish) {
      emits('finished')
    }
  } catch (error) {
    console.error('上传失败', error);
  }
}
const removeSelectedFile = () => {
  let values: any = null;
  if (Array.isArray(value.value)) {
    values = []
  }
  emits('update:value', values);
}
defineExpose({click})
</script>

<template>
  <div class="custom-upload"
       :style="{'--upload-box-width':width + 'px','--upload-box-height':height + 'px','--upload-box-border-style':borderStyle,'--upload-box-border-color':hasFile?ThemesColor.appTheme:'#dde5f2'}">
    <n-upload v-bind="props"
              ref="uploadRef"
              :accept="acceptType"
              :action="action"
              :default-upload="false"
              :show-file-list="false" @change="uploadFile">
      <template v-if="type==undefined">
        <div ref="triggerDiv"></div>
      </template>
      <template v-else>
        <template v-if="type==='box'">
          <div class="custom-upload-box">
            <div class="custom-upload-box-remove" v-if="hasFile" @click.stop="removeSelectedFile">
              <n-icon color="#fff" :size="16">
                <Close/>
              </n-icon>
            </div>
            <template v-if="hasFile">
              <n-icon :size="48" :color="ThemesColor.appTheme">
                <DocumentTableCheckmark20Filled/>
              </n-icon>
            </template>
            <template v-else>
              <n-icon color="#dde5f2" :size="48">
                <FileUploadRound/>
              </n-icon>
            </template>
          </div>
        </template>
        <template v-if="type==='button'">
          <n-button type="primary">
            <template #icon>
              <n-icon v-if="hasFile">
                <CheckCircleRound/>
              </n-icon>
              <n-icon v-else>
                <FileUploadRound/>
              </n-icon>
            </template>
            <template #default>
              {{
                hasFile ? '已上传' + uploadText : upFileType ? '上传' + uploadText + '(' + upFileType + ')' : '上传' + uploadText
              }}
            </template>
          </n-button>
        </template>
        <template v-if="type==='slot'">
          <slot name="default"></slot>
        </template>
      </template>
    </n-upload>
  </div>
</template>

<style lang="scss">
.custom-upload {
  &-box {
    width: var(--upload-box-width);
    height: var(--upload-box-height);
    background-color: #FFFFFF;
    border-radius: var(--n-border-radius);
    border: 1px var(--upload-box-border-style) var(--upload-box-border-color);
    display: flex;
    justify-content: center;
    align-items: center; /* 垂直居中 */
    cursor: pointer;
    padding: 2px;
    position: relative;

    &-remove {
      cursor: pointer;
      width: 16px;
      height: 16px;
      background-color: rgba(0, 0, 0, .3);
      position: absolute;
      top: -8px;
      right: -8px;
      border-radius: 50%;
      text-align: center;
    }
  }

}
</style>